// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"github.com/gorilla/websocket"
	"io"
	"log"
	"net/http"
	"strconv"
	"time"
	"strings"
)

type Response struct {
	Code     int    `json:"code"`
	Message  string `json:"message"`
	Type     int    `json:"type"`
	To       int    `json:"to"`
	Uid      int    `json:"uid"`
	Remark   string `json:"remark"`
	ReportId int    `json:"report_id"`
	Tos string `json:"tos`
}

type Ret struct{
	Code int
	Param string
	Msg string
}
var uidFromClients = make(map[int]map[*Client]*Client)
var uidFromClientFormate = make(map[*Client]int)

type Hub struct {
	// Registered clients.
	clients map[*Client]bool

	// Inbound messages from the clients.
	broadcast chan []byte

	// client to bind uid

	// Register requests from the clients.
	register chan *Client

	// Unregister requests from clients.
	unregister chan *Client
}

const (
	// Time allowed to write a message to the peer.
	writeWait = 10 * time.Second

	// Time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second

	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10

	// Maximum message size allowed from peer.
	maxMessageSize = 512
)

var (
	newline = []byte{'\n'}
	space   = []byte{' '}
)

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

// Client is a middleman between the websocket connection and the hub.
type Client struct {
	hub *Hub

	// The websocket connection.
	conn *websocket.Conn

	// Buffered channel of outbound messages.
	send chan []byte
}

// readPump pumps messages from the websocket connection to the hub.
//
// The application runs readPump in a per-connection goroutine. The application
// ensures that there is at most one reader on a connection by executing all
// reads from this goroutine.
func (c *Client) readPump() {
	defer func() {
		c.hub.unregister <- c
		c.conn.Close()
	}()
	c.conn.SetReadLimit(maxMessageSize)
	c.conn.SetReadDeadline(time.Now().Add(pongWait))
	c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	for {
		_, message, err := c.conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				log.Printf("error: %v", err)
			}
			break
		}
		resp := &Response{}
		jsonErr := json.Unmarshal(message, resp)
		if jsonErr == nil && resp.Code != 0 {
			if resp.Code == 1 {
				if resp.Uid != 0 {
					if _, ok := uidFromClients[resp.Uid]; !ok {
						fmt.Println(11)
						cli := make(map[*Client]*Client)
						uidFromClients[resp.Uid] = cli
					}
					uidFromClients[resp.Uid][c] =  c
					uidFromClientFormate[c] = resp.Uid
					c.send <- []byte(`{"code": 0, "message": "绑定UID成功"}`)
				}
			} else if resp.Code == 2 {
				if resp.To != 0 {
					if len(uidFromClients[resp.To]) > 0 {
						for k, _ := range uidFromClients[resp.To] {
							k.send <- message
						}
					}
				}
			} else {
				c.hub.broadcast <- []byte(resp.Message)
			}
		} else {
			message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
			c.hub.broadcast <- message
		}

	}
}

// writePump pumps messages from the hub to the websocket connection.
//
// A goroutine running writePump is started for each connection. The
// application ensures that there is at most one writer to a connection by
// executing all writes from this goroutine.
func (c *Client) writePump() {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		c.conn.Close()
	}()
	for {
		select {
		case message, ok := <-c.send:
			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
			if !ok {
				// The hub closed the channel.
				c.conn.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}
			c.conn.WriteMessage(websocket.TextMessage, message)
			// 心跳
		case t := <-ticker.C:
			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
			if err := c.conn.WriteMessage(websocket.PingMessage, []byte(t.String())); err != nil {
				fmt.Println("heartbeat err:", err)
				return
			}
		}
	}
}

// serveWs handles websocket requests from the peer.
func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}
	Clients := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}
	Clients.hub.register <- Clients

	// Allow collection of memory referenced by the caller by doing all work in
	// new goroutines.
	go Clients.writePump()
	go Clients.readPump()
}

func newHub() *Hub {
	return &Hub{
		broadcast:  make(chan []byte),
		register:   make(chan *Client),
		unregister: make(chan *Client),
		clients:    make(map[*Client]bool),
	}
}

func (h *Hub) run() {
	for {
		select {
		case client := <-h.register:
			h.clients[client] = true
			time.Sleep(1e9)
			client.conn.WriteMessage(websocket.TextMessage, []byte(`{"code": 0, "message": "链接成功"}`))
		case client := <-h.unregister:
			if _, ok := h.clients[client]; ok {
				delete(h.clients, client)
				delete(uidFromClients[uidFromClientFormate[client]], client)
				delete(uidFromClientFormate, client)
				close(client.send)
				fmt.Println(uidFromClients, uidFromClientFormate)
			}
		case message := <-h.broadcast:
			for client := range h.clients {
				select {
				case client.send <- message:
				default:
					close(client.send)
					delete(h.clients, client)
				}
			}
		}
	}
}

var addr = flag.String("addr", ":18301", "http service address")

func serveHome(w http.ResponseWriter, r *http.Request) {
	log.Println(r.URL)
	if r.URL.Path != "/" {
		http.Error(w, "Not found", http.StatusNotFound)
		return
	}
	if r.Method != "GET" {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}
	http.ServeFile(w, r, "home.html")
}

func main() {
	flag.Parse()
	hub := newHub()
	go hub.run()
	http.HandleFunc("/", serveHome)
	http.HandleFunc("/send", send)
	http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
		serveWs(hub, w, r)
	})
	err := http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func send(w http.ResponseWriter, r *http.Request) {
	var (
		to, uid string
	)
	query := r.URL.Query()
	if len(query["msg"]) == 0 {
		w.Write([]byte(`{"code": 403, "message": "缺少msg参数"}`))
		return
	}
	if len(query["code"]) == 0 {
		w.Write([]byte(`{"code": 403, "message": "缺少code参数"}`))
		return
	}
	if len(query["to"]) != 0 {
		to = query["to"][0]
	}
	if len(query["uid"]) != 0 {
		uid = query["uid"][0]
	}

	msg := query["msg"][0]
	code := query["code"][0]
	resp := &Response{}
	if len(query["remark"]) != 0 {
		resp.Remark = query["remark"][0]
	}
	if len(query["report_id"]) != 0 {
		resp.ReportId, _ = strconv.Atoi(query["report_id"][0])
	}
	if len(query["tos"]) != 0 {
		resp.Tos = query["tos"][0]
	}
	resp.Code, _ = strconv.Atoi(code)
	resp.To, _ = strconv.Atoi(to)
	resp.Message = msg
	resp.Uid, _ = strconv.Atoi(uid)
	//fmt.Println(resp)
	fmt.Println(SendTo(resp))
	ret := new(Ret)
	ret.Code = 0
	ret.Msg = "success"
	retJson,_ := json.Marshal(ret)
	w.Header().Set("content-type","application/json")
	io.WriteString(w, string(retJson))
}

func SendTo(msg *Response) error {
	if msg.Code == 0 {
		return errors.New("Code is empty")
	}
	fmt.Println("发送消息", uidFromClients , "\n")
	data, jsonErr := json.Marshal(msg)
	if jsonErr != nil {
		return jsonErr
	}
	switch msg.Code {
	case 2: //单发
		_, ok := uidFromClients[msg.To]
		if ok {
			for k, _ := range uidFromClients[msg.To] {
				k.send <- []byte(data)
			}
		}
	case 3: //多发
		users := strings.Split(msg.Tos,",")
		if len(users) >0 {
			for _, u := range users {
				uid, _ := strconv.Atoi(u)
				if _, ok := uidFromClients[uid]; ok {
					for usk, _ := range uidFromClients[uid] {
						usk.send <- []byte(data)
					}
				}
			}
		}
	default: //群发
		if len(uidFromClients) > 0 {
			for _, capital := range uidFromClients {
				if len(capital) > 0 {
					for usk, _ := range capital {
						usk.send <- []byte(data)
					}
				}
			}
		}
	}
	return nil
}
